package com.huqing.icu.aop;

import com.alibaba.fastjson.JSON;
import com.huqing.icu.exceptions.BusinessException;
import com.huqing.icu.utils.CustomWebUtils;
import com.huqing.icu.utils.IpUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;

/**
 * 拦截Controller，打印每个请求的日子
 */
@Component
@Aspect
@Slf4j
public class ControllerAop {

    @Value(value = "${log.show:true}")
    private boolean logShow;

    /*
     * 只拦截RestController标记的方法
     */
    @Pointcut("@within(org.springframework.web.bind.annotation.RestController) || @annotation(org.springframework.web.bind.annotation.RestController)")
    public void requestServer() {
    }

    //@Around("requestServer()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 计时
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        Object result = proceedingJoinPoint.proceed();
        if (!logShow) {
            return result;
        }
        String ip = IpUtil.getIpAddress(request);
        String url = request.getRequestURL().toString();
        /*if (url.contains("/api/log")) {
            return result;
        }*/
        Long userId = (Long) request.getAttribute("userId");
        Map<String, Object> params = getRequestParamsByProceedingJoinPoint(proceedingJoinPoint);
        Map<String, String> map = this.getHeadersInfo(request);
        StringBuffer sb = new StringBuffer("\n");

        sb.append("用户ip地址为: ").append(ip).append("\n");
        sb.append("url路径为: ").append(url).append("\n");
        sb.append("请求head: ").append(JSON.toJSONString(map)).append("\n");
        sb.append("sessionId: ").append(CustomWebUtils.getSessionId(request)).append("\n");
        sb.append("用户userId: ").append(userId).append("\n");
        sb.append("入参为: ").append(params).append("\n");
        List<String> list = Arrays.asList("/api/third_platform/user_partner_list");
        for (String url1 : list) {
            if (url.contains(url1)) {
                log.info("请求日志：{\n" + sb.toString() + "\n}");
                return result;
            }
        }
        log.info("请求日志：{\n" + sb.toString() + "\n}");
        sb.append("返回结果: ").append(JSON.toJSONString(result)).append("\n");

        stopWatch.stop();
        long totalTimeMillis = stopWatch.getTotalTimeMillis();
        log.info("request end, url: {}, cost: {}ms", url, totalTimeMillis);
        log.info("请求日志：{\n" + sb.toString() + "\n}");
        return result;
    }


    //@AfterThrowing(pointcut = "requestServer()", throwing = "e")
    public void doAfterThrow(JoinPoint joinPoint, RuntimeException e) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String ip = IpUtil.getIpAddress(request);
        String url = request.getRequestURL().toString();
        if (url.contains("/api/log")) {
            return;
        }
        String method = request.getMethod();
        Long userId = (Long) request.getAttribute("userId");
        String classMethod = String.format("%s.%s", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
        Map<String, Object> params = getRequestParamsByJoinPoint(joinPoint);
        Map<String, String> map = this.getHeadersInfo(request);
        StringBuffer sb = new StringBuffer("\n");

        sb.append("用户ip地址为: ").append(ip).append("\n");
        sb.append("url路径为: ").append(url).append("\n");
        sb.append("请求方法为: ").append(method).append("\n");
        sb.append("接口路径为: ").append(classMethod).append("\n");
        sb.append("请求head: ").append(JSON.toJSONString(map)).append("\n");
        sb.append("用户userId: ").append(userId).append("\n");
        sb.append("入参为: ").append(params).append("\n");
        if (e instanceof BusinessException) {
            log.info("请求日志：{\n" + sb.toString() + "\n}");
            return;
        }
        sb.append("Exception: ").append(JSON.toJSONString(e)).append("\n");
        log.info("请求日志：{\n" + sb.toString() + "\n}");

    }

    private Map<String, Object> getRequestParamsByJoinPoint(JoinPoint joinPoint) {
        //参数名
        String[] paramNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
        //参数值
        Object[] paramValues = joinPoint.getArgs();

        return buildRequestParam(paramNames, paramValues);
    }

    private Map<String, String> getHeadersInfo(HttpServletRequest request) {
        Map<String, String> map = new HashMap<String, String>();
        Enumeration<?> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = (String) headerNames.nextElement();
            String value = request.getHeader(key);
            map.put(key, value);
        }
        return map;
    }

    /**
     * 获取入参
     *
     * @param proceedingJoinPoint
     * @return
     */
    private Map<String, Object> getRequestParamsByProceedingJoinPoint(ProceedingJoinPoint proceedingJoinPoint) {
        //参数名
        String[] paramNames = ((MethodSignature) proceedingJoinPoint.getSignature()).getParameterNames();
        //参数值
        Object[] paramValues = proceedingJoinPoint.getArgs();

        return buildRequestParam(paramNames, paramValues);
    }

    private Map<String, Object> buildRequestParam(String[] paramNames, Object[] paramValues) {
        Map<String, Object> requestParams = new HashMap<>();
        for (int i = 0; i < paramNames.length; i++) {
            Object value = paramValues[i];

            //如果是文件对象
            if (value instanceof MultipartFile) {
                MultipartFile file = (MultipartFile) value;
                value = file.getOriginalFilename();  //获取文件名
            }

            requestParams.put(paramNames[i], value);
        }

        return requestParams;
    }
}
